package dk.itu.mario.level;

import java.util.Random;

import dk.itu.mario.MarioInterface.Constraints;
import dk.itu.mario.MarioInterface.GamePlay;
import dk.itu.mario.MarioInterface.LevelInterface;
import dk.itu.mario.engine.sprites.SpriteTemplate;
import dk.itu.mario.engine.sprites.Enemy;


public class MyLevel extends Level{
	//Store information about the level
	public int ENEMIES = 0; //the number of enemies the level contains
	public int BLOCKS_EMPTY = 0; // the number of empty blocks
	public int BLOCKS_COINS = 0; // the number of coin blocks
	public int BLOCKS_POWER = 0; // the number of power blocks
	public int COINS = 0; //These are the coins in boxes that Mario collect

    // the player classes
    private static final int NORMAL = 0;
    private static final int COIN_COLLECTOR = 1;
    private static final int KILLER = 2;
    private static final int NOOB = 3;
    private static final int AIMLESS_JUMPER = 4;
    private static final int BONUS = 5;
 
    // probabilities
    private static double HAS_HILL = 0.0;
    private static double HAS_BLOCKS = 0.0;
    private static double HAS_ENEMIES = 0.0;
    private static double HAS_CANNONS = 0.0;
    private static double HAS_COINS = 0.0;
    private static double HAS_PIPES = 0.0;
    private static double HAS_PITS = 0.0;
    private static double IS_PLAIN = 0.0;
    
    private static int PLAYER_PROFILE;
    
    private static boolean justHadPit = false;
 
	private static int getPlayerProfile(GamePlay playerMetrics) {
      //TODO: look at playerMetrics to determine what kind of Mario we're dealing with
      
      // killer
      int totalEnemies = playerMetrics.totalEnemies;
      int redTurtlesKilled = playerMetrics.RedTurtlesKilled;
      int greenTurtlesKilled = playerMetrics.GreenTurtlesKilled;
      int armoredTurtlesKilled = playerMetrics.ArmoredTurtlesKilled;
      int goombasKilled = playerMetrics.GoombasKilled;
      int cannonBallKilled = playerMetrics.CannonBallKilled;
      int jumpFlowersKilled = playerMetrics.JumpFlowersKilled;
      int chompFlowersKilled = playerMetrics.ChompFlowersKilled;
      int totalKilled = redTurtlesKilled
          + greenTurtlesKilled
          + armoredTurtlesKilled
          + goombasKilled
          + cannonBallKilled
          + jumpFlowersKilled
          + chompFlowersKilled;
      double percentKilled = (double) totalKilled / totalEnemies;
      
      //collector
      int totalCoins = playerMetrics.totalCoins;
      int coinsCollected = playerMetrics.coinsCollected;
      double percentCoins = (double) coinsCollected / totalCoins;
      
      //noob
      int timesOfDeathByFallingIntoGap = (int) playerMetrics.timesOfDeathByFallingIntoGap;
      int timesOfDeathByRedTurtle = playerMetrics.timesOfDeathByRedTurtle;
      int timesOfDeathByGoomba = playerMetrics.timesOfDeathByGoomba;
      int timesOfDeathByGreenTurtle = playerMetrics.timesOfDeathByGreenTurtle;
      int timesOfDeathByArmoredTurtle = playerMetrics.timesOfDeathByArmoredTurtle;
      int timesOfDeathByJumpFlower = playerMetrics.timesOfDeathByJumpFlower;
      int timesOfDeathByCannonBall = playerMetrics.timesOfDeathByCannonBall;
      int timesOfDeathByChompFlower = playerMetrics.timesOfDeathByChompFlower;
      int totalDeaths = timesOfDeathByFallingIntoGap 
          + timesOfDeathByRedTurtle
          + timesOfDeathByGoomba
          + timesOfDeathByGreenTurtle
          + timesOfDeathByArmoredTurtle
          + timesOfDeathByJumpFlower
          + timesOfDeathByCannonBall
          + timesOfDeathByChompFlower;
          
      if (totalDeaths >= 3) {
        return NOOB;
      } else if (percentCoins >= 1) {
        //HAS_
        return COIN_COLLECTOR;
      } else if (percentKilled >= 0.8) {
        return KILLER;
      } else {
        return NORMAL;
      }
    }
    
    private static Random levelSeedRandom = new Random();
	    public static long lastSeed;

	    Random random;

  
	    private int difficulty;
	    private int type;
		private int gaps;
		
		public MyLevel(int width, int height)
	    {
			super(width, height);
	    }


		public MyLevel(int width, int height, long seed, int difficulty, int type, GamePlay playerMetrics)
	    {
	        this(width, height);
	        creat(seed, difficulty, type, playerMetrics);
	    }

	    public void creat(long seed, int difficulty, int type, GamePlay playerMetrics)
	    {
	        this.type = type;
	        this.difficulty = difficulty;
	        lastSeed = seed;
	        random = new Random(seed);
            
            int playerProfile = KILLER;//getPlayerProfile(playerMetrics);
            if (playerProfile == NOOB) {
                HAS_HILL = 0.0;
                HAS_BLOCKS = 0.8;
                HAS_ENEMIES = 0.4;
                HAS_CANNONS = 0.0;
                HAS_COINS = 0.9;
                HAS_PIPES = 0.0;
                HAS_PITS = 0.0;
                IS_PLAIN = 1.0;
            } else if (playerProfile == COIN_COLLECTOR) {
                HAS_HILL = 0.2;
                HAS_BLOCKS = 1.0;
                HAS_ENEMIES = 0.4;
                HAS_CANNONS = 0.0;
                HAS_COINS = 1.0;
                HAS_PIPES = 0.2;
                HAS_PITS = 0.5; 
                IS_PLAIN = 0.4;                
            } else if (playerProfile == KILLER) {
                HAS_HILL = 0.4;
                HAS_BLOCKS = 0.9;
                HAS_ENEMIES = 1.0;
                HAS_CANNONS = 0.2;
                HAS_COINS = 0.6;
                HAS_PIPES = 0.4;
                HAS_PITS = 0.4; 
                IS_PLAIN = 0.4;                
            } else if (playerProfile == AIMLESS_JUMPER) {
                HAS_HILL = 0.8;
                HAS_BLOCKS = 1.0;
                HAS_ENEMIES = 0.2;
                HAS_CANNONS = 0.2;
                HAS_COINS = 0.6;
                HAS_PIPES = 0.2;
                HAS_PITS = 0.9;    
                IS_PLAIN = 0.1;                
            } else if (playerProfile == BONUS) {
                HAS_HILL = 0.0;
                HAS_BLOCKS = 0.0;
                HAS_ENEMIES = 0.0;
                HAS_CANNONS = 0.9;
                HAS_COINS = 0.0;
                HAS_PIPES = 0.0;
                HAS_PITS = 1.0;   
                IS_PLAIN = 0.0;                
            } else {
                HAS_HILL = 0.1;
                HAS_BLOCKS = 0.8;
                HAS_ENEMIES = 0.8;
                HAS_CANNONS = 0.1;
                HAS_COINS = 0.8;
                HAS_PIPES = 0.1;
                HAS_PITS = 0.1; 
                IS_PLAIN = 0.1;
            }
            PLAYER_PROFILE = KILLER;
            System.out.println(playerProfile);
            
	        //create the start location
	        int length = 0;
	        length += buildStraight(0, width, true);
            
            while (length < width - 64) {
              length += buildCustom(length, width-length, playerProfile);
              length += buildCustom(length, width-length, playerProfile);
              length += buildCustom(length, width-length, playerProfile);
              length += buildCustom(length, width-length, playerProfile);
              length += buildCustom(length, width-length, playerProfile);
              length += buildCustom(length, width-length, playerProfile);
            }
            
            int floor = height - 1;
            
            xExit = length + 8;
	        yExit = floor;

            // fills the end piece
	        for (int x = length; x < width; x++)
	        {
	            for (int y = 0; y < height; y++)
	            {
	                if (y >= floor)
	                {
	                    setBlock(x, y, GROUND);
	                }
	            }
	        }
            
            fixWalls();
            
            /*
	        //create all of the medium sections
	        while (length < width - 64)
	        {
	            //length += buildZone(length, width - length);
				length += buildStraight(length, width-length, false);
				length += buildStraight(length, width-length, false);
				length += buildHillStraight(length, width-length);
				length += buildJump(length, width-length);
				length += buildTubes(length, width-length);
				length += buildCannons(length, width-length);
	        }

	        //set the end piece
	        int floor = height - 1 - random.nextInt(4);

	        xExit = length + 8;
	        yExit = floor;

	        // fills the end piece
	        for (int x = length; x < width; x++)
	        {
	            for (int y = 0; y < height; y++)
	            {
	                if (y >= floor)
	                {
	                    setBlock(x, y, GROUND);
	                }
	            }
	        }

	        if (type == LevelInterface.TYPE_CASTLE || type == LevelInterface.TYPE_UNDERGROUND)
	        {
	            int ceiling = 0;
	            int run = 0;
	            for (int x = 0; x < width; x++)
	            {
	                if (run-- <= 0 && x > 4)
	                {
	                    ceiling = random.nextInt(4);
	                    run = random.nextInt(4) + 4;
	                }
	                for (int y = 0; y < height; y++)
	                {
	                    if ((x > 4 && y <= ceiling) || x < 1)
	                    {
	                        setBlock(x, y, GROUND);
	                    }
	                }
	            }
	        }

	        fixWalls();
            */
	    }


	    private int buildJump(int xo, int maxLength)
	    {	gaps++;
	    	//jl: jump length
	    	//js: the number of blocks that are available at either side for free
	        int js = random.nextInt(4) + 2;
	        int jl = random.nextInt(2) + 2;
	        int length = js * 2 + jl;

	        boolean hasStairs = random.nextInt(3) == 0;

	        int floor = height - 1 - random.nextInt(4);
	      //run from the start x position, for the whole length
	        for (int x = xo; x < xo + length; x++)
	        {
	            if (x < xo + js || x > xo + length - js - 1)
	            {
	            	//run for all y's since we need to paint blocks upward
	                for (int y = 0; y < height; y++)
	                {	//paint ground up until the floor
	                    if (y >= floor)
	                    {
	                        setBlock(x, y, GROUND);
	                    }
	                  //if it is above ground, start making stairs of rocks
	                    else if (hasStairs)
	                    {	//LEFT SIDE
	                        if (x < xo + js)
	                        { //we need to max it out and level because it wont
	                          //paint ground correctly unless two bricks are side by side
	                            if (y >= floor - (x - xo) + 1)
	                            {
	                                setBlock(x, y, ROCK);
	                            }
	                        }
	                        else
	                        { //RIGHT SIDE
	                            if (y >= floor - ((xo + length) - x) + 2)
	                            {
	                                setBlock(x, y, ROCK);
	                            }
	                        }
	                    }
	                }
	            }
	        }

	        return length;
	    }

	    private int buildCannons(int xo, int maxLength)
	    {
	        int length = random.nextInt(10) + 2;
	        if (length > maxLength) length = maxLength;

	        int floor = height - 1 - random.nextInt(4);
	        int xCannon = xo + 1 + random.nextInt(4);
	        for (int x = xo; x < xo + length; x++)
	        {
	            if (x > xCannon)
	            {
	                xCannon += 2 + random.nextInt(4);
	            }
	            if (xCannon == xo + length - 1) xCannon += 10;
	            int cannonHeight = floor - random.nextInt(4) - 1;

	            for (int y = 0; y < height; y++)
	            {
	                if (y >= floor)
	                {
	                    setBlock(x, y, GROUND);
	                }
	                else
	                {
	                    if (x == xCannon && y >= cannonHeight)
	                    {
	                        if (y == cannonHeight)
	                        {
	                            setBlock(x, y, (byte) (14 + 0 * 16));
	                        }
	                        else if (y == cannonHeight + 1)
	                        {
	                            setBlock(x, y, (byte) (14 + 1 * 16));
	                        }
	                        else
	                        {
	                            setBlock(x, y, (byte) (14 + 2 * 16));
	                        }
	                    }
	                }
	            }
	        }

	        return length;
	    }

	    private int buildHillStraight(int xo, int maxLength)
	    {
	        int length = random.nextInt(10) + 10;
	        if (length > maxLength) length = maxLength;

	        int floor = height - 1 - random.nextInt(4);
	        for (int x = xo; x < xo + length; x++)
	        {
	            for (int y = 0; y < height; y++)
	            {
	                if (y >= floor)
	                {
	                    setBlock(x, y, GROUND);
	                }
	            }
	        }

	        addEnemyLine(xo + 1, xo + length - 1, floor - 1);

	        int h = floor;

	        boolean keepGoing = true;

	        boolean[] occupied = new boolean[length];
	        while (keepGoing)
	        {
	            h = h - 2 - random.nextInt(3);

	            if (h <= 0)
	            {
	                keepGoing = false;
	            }
	            else
	            {
	                int l = random.nextInt(5) + 3;
	                int xxo = random.nextInt(length - l - 2) + xo + 1;

	                if (occupied[xxo - xo] || occupied[xxo - xo + l] || occupied[xxo - xo - 1] || occupied[xxo - xo + l + 1])
	                {
	                    keepGoing = false;
	                }
	                else
	                {
	                    occupied[xxo - xo] = true;
	                    occupied[xxo - xo + l] = true;
	                    addEnemyLine(xxo, xxo + l, h - 1);
	                    if (random.nextInt(4) == 0)
	                    {
	                        decorate(xxo - 1, xxo + l + 1, h);
	                        keepGoing = false;
	                    }
	                    for (int x = xxo; x < xxo + l; x++)
	                    {
	                        for (int y = h; y < floor; y++)
	                        {
	                            int xx = 5;
	                            if (x == xxo) xx = 4;
	                            if (x == xxo + l - 1) xx = 6;
	                            int yy = 9;
	                            if (y == h) yy = 8;

	                            if (getBlock(x, y) == 0)
	                            {
	                                setBlock(x, y, (byte) (xx + yy * 16));
	                            }
	                            else
	                            {
	                                if (getBlock(x, y) == HILL_TOP_LEFT) setBlock(x, y, HILL_TOP_LEFT_IN);
	                                if (getBlock(x, y) == HILL_TOP_RIGHT) setBlock(x, y, HILL_TOP_RIGHT_IN);
	                            }
	                        }
	                    }
	                }
	            }
	        }

	        return length;
	    }

	    private void addEnemyLine(int x0, int x1, int y)
	    {
	        for (int x = x0; x < x1; x++)
	        {
                if (random.nextInt(35) < difficulty + 1 || (PLAYER_PROFILE == KILLER && random.nextInt(3) == 0))
	            {
	                int type = random.nextInt(4);

	                if (PLAYER_PROFILE == NOOB)
	                {
	                    type = Enemy.ENEMY_GOOMBA;
	                }
                    else if (PLAYER_PROFILE == KILLER) {
                        type = random.nextInt(4);
                    }
	                else
	                {
	                    type = random.nextInt(3);
	                }

	                setSpriteTemplate(x, y, new SpriteTemplate(type, random.nextInt(35) < difficulty));
	                ENEMIES++;
	            }
	        }
	    }

	    private int buildTubes(int xo, int maxLength)
	    {
	        int length = random.nextInt(10) + 5;
	        if (length > maxLength) length = maxLength;

	        int floor = height - 1 - random.nextInt(4);
	        int xTube = xo + 1 + random.nextInt(4);
	        int tubeHeight = floor - random.nextInt(2) - 2;
	        for (int x = xo; x < xo + length; x++)
	        {
	            if (x > xTube + 1)
	            {
	                xTube += 3 + random.nextInt(4);
	                tubeHeight = floor - random.nextInt(2) - 2;
	            }
	            if (xTube >= xo + length - 2) xTube += 10;

	            if (x == xTube && random.nextInt(11) < difficulty + 1)
	            {
	                setSpriteTemplate(x, tubeHeight, new SpriteTemplate(Enemy.ENEMY_FLOWER, false));
	                ENEMIES++;
	            }

	            for (int y = 0; y < height; y++)
	            {
	                if (y >= floor)
	                {
	                    setBlock(x, y,GROUND);

	                }
	                else
	                {
	                    if ((x == xTube || x == xTube + 1) && y >= tubeHeight)
	                    {
	                        int xPic = 10 + x - xTube;

	                        if (y == tubeHeight)
	                        {
	                        	//tube top
	                            setBlock(x, y, (byte) (xPic + 0 * 16));
	                        }
	                        else
	                        {
	                        	//tube side
	                            setBlock(x, y, (byte) (xPic + 1 * 16));
	                        }
	                    }
	                }
	            }
	        }

	        return length;
	    }

	    private int buildStraight(int xo, int maxLength, boolean safe)
	    {
	        int length = random.nextInt(10) + 2;

	        if (safe)
	        	length = 10 + random.nextInt(5);

	        if (length > maxLength)
	        	length = maxLength;

	        int floor = height - 1 - random.nextInt(4);

	        //runs from the specified x position to the length of the segment
	        for (int x = xo; x < xo + length; x++)
	        {
	            for (int y = 0; y < height; y++)
	            {
	                if (y >= floor)
	                {
	                    setBlock(x, y, GROUND);
	                }
	            }
	        }

	        if (!safe)
	        {
	            if (length > 5)
	            {
	                decorate(xo, xo + length, floor);
	            }
	        }

	        return length;
	    }
        
        private int buildCustom(int xo, int maxLength, int playerProfile)
	    {
	        int length = random.nextInt(10) + 2;

            if (length > maxLength)
	        	length = maxLength;
            
            boolean isPlain = false;
            boolean hasCannons = false;
            boolean hasPipes = false;
            boolean hasHill = false;
            boolean hasPits = false;
            boolean hasBlocks = false;
            boolean hasSpecialBlocks = false;
            boolean hasEnemies = false;
            boolean hasCoins = false;
            boolean hasCoinArch = false;
            
            isPlain = random.nextInt(10) < 10 * IS_PLAIN;
            
            if (!isPlain) {
            
                // if these are true, nothing else can be
                hasCannons = random.nextInt(10) < 10 * HAS_CANNONS;
                hasPipes = random.nextInt(10) < 10 * HAS_PIPES; 
                if (hasPipes && hasCannons) {
                    if (random.nextInt(2) == 0)
                        hasCannons = false;
                    else
                        hasPipes = false;
                }
                if (hasPipes)
                    hasEnemies = random.nextInt(10) < 10 * HAS_ENEMIES;
                if (!hasPipes && !hasCannons) {
                    hasHill = random.nextInt(10) < 10 * HAS_HILL;
                    hasPits = random.nextInt(10) < 10 * HAS_PITS && !justHadPit;
                    if (hasHill && hasPits) {
                        if (random.nextInt(2) == 0)
                            hasHill = false;
                        else
                            hasPipes = false;
                    }

                    hasBlocks = random.nextInt(10) < 10 * HAS_BLOCKS && (!hasPits || playerProfile == NOOB);
                    hasSpecialBlocks = hasBlocks && !hasPits;
                    hasEnemies = random.nextInt(10) < 10 * HAS_ENEMIES;
                    hasCoins = random.nextInt(10) < 10 * HAS_COINS;
                    hasCoinArch = hasCoins && hasPits && !hasBlocks;
                }          
            } else {
                hasBlocks = random.nextInt(10) < 10 * HAS_BLOCKS;
                hasCoins = random.nextInt(10) < 10 * HAS_COINS;
                hasEnemies = random.nextInt(10) < 10 * HAS_ENEMIES;
            }
                
            /*
            cannons & pipes can't be with anything
            pits can't be with hills
            pits can't be with special block
            coins can't be with blocks
            special blocks can't be with pits
            */
                       
            double js = length / 2.0;
            int jl = 0;
            boolean hasStairs = false;                        
            
            int floor = height - 1 - random.nextInt(4);

            if (hasPits) {
                justHadPit = true;
                gaps++;
                js = (double) random.nextInt(4) + 2;
                jl = random.nextInt(2) + 2;
                length = (int) js * 2 + jl;
                hasStairs = random.nextInt(3) == 0;
            } else {
                justHadPit = false;
            }
            
            if (hasHill) {
                length = random.nextInt(10) + 10;
                if (length > maxLength) length = maxLength;

                floor = height - 1 - random.nextInt(4);

                for (int x = xo; x < xo + length; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                    }
                }
            
                if (hasEnemies)
                    addEnemyLine(xo + 1, xo + length - 1, floor - 1);

                int h = floor;

                boolean keepGoing = true;

                boolean[] occupied = new boolean[length];
                while (keepGoing)
                {
                    h = h - 2 - random.nextInt(3);

                    if (h <= 0)
                    {
                        keepGoing = false;
                    }
                    else
                    {
                        int l = random.nextInt(5) + 3;
                        int xxo = random.nextInt(length - l - 2) + xo + 1;

                        if (occupied[xxo - xo] || occupied[xxo - xo + l] || occupied[xxo - xo - 1] || occupied[xxo - xo + l + 1])
                        {
                            keepGoing = false;
                        }
                        else
                        {
                            occupied[xxo - xo] = true;
                            occupied[xxo - xo + l] = true;
                            if (hasEnemies)
                                addEnemyLine(xxo, xxo + l, h - 1);
                            if (random.nextInt(4) == 0)
                            {
                                keepGoing = false;
                            }
                            for (int x = xxo; x < xxo + l; x++)
                            {
                                for (int y = h; y < floor; y++)
                                {
                                    int xx = 5;
                                    if (x == xxo) xx = 4;
                                    if (x == xxo + l - 1) xx = 6;
                                    int yy = 9;
                                    if (y == h) yy = 8;

                                    if (getBlock(x, y) == 0)
                                    {
                                        setBlock(x, y, (byte) (xx + yy * 16));
                                    }
                                    else
                                    {
                                        if (getBlock(x, y) == HILL_TOP_LEFT) setBlock(x, y, HILL_TOP_LEFT_IN);
                                        if (getBlock(x, y) == HILL_TOP_RIGHT) setBlock(x, y, HILL_TOP_RIGHT_IN);
                                    }
                                }
                            }
                        }
                    }
                }
            } 
            if (hasCannons) {
                buildCannons(xo, maxLength);
            } 
            if (hasPipes) {
                length = random.nextInt(10) + 5;
                if (length > maxLength) 
                    length = maxLength;

                floor = height - 1 - random.nextInt(4);
                int xTube = xo + 1 + random.nextInt(4);
                int tubeHeight = floor - random.nextInt(2) - 2;
                for (int x = xo; x < xo + length; x++)
                {
                    if (x > xTube + 1)
                    {
                        xTube += 3 + random.nextInt(4);
                        tubeHeight = floor - random.nextInt(2) - 2;
                    }
                    if (xTube >= xo + length - 2) xTube += 10;

                    if (x == xTube && hasEnemies)
                    {
                        setSpriteTemplate(x, tubeHeight, new SpriteTemplate(Enemy.ENEMY_FLOWER, false));
                        ENEMIES++;
                    }

                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y,GROUND);

                        }
                        else
                        {
                            if ((x == xTube || x == xTube + 1) && y >= tubeHeight)
                            {
                                int xPic = 10 + x - xTube;

                                if (y == tubeHeight)
                                {
                                    //tube top
                                    setBlock(x, y, (byte) (xPic + 0 * 16));
                                }
                                else
                                {
                                    //tube side
                                    setBlock(x, y, (byte) (xPic + 1 * 16));
                                }
                            }
                        }
                    }
                }
            } 
            if (true) {
            
                //runs from the specified x position to the length of the segment
                for (int x = xo; x < xo + length; x++)
                {                
                    if (x < xo + js || x > xo + length - js - 1) {
                        //run for all y's since we need to paint blocks upward
                        for (int y = 0; y < height; y++)
                        {	//paint ground up until the floor
                            if (y >= floor)
                            {
                                setBlock(x, y, GROUND);
                            }
                          //if it is above ground, start making stairs of rocks
                            else if (hasStairs)
                            {	//LEFT SIDE
                                if (x < xo + js)
                                { //we need to max it out and level because it wont
                                  //paint ground correctly unless two bricks are side by side
                                    if (y >= floor - (x - xo) + 1)
                                    {
                                        setBlock(x, y, ROCK);
                                    }
                                }
                                else
                                { //RIGHT SIDE
                                    if (y >= floor - ((xo + length) - x) + 2)
                                    {
                                        setBlock(x, y, ROCK);
                                    }
                                }
                            }
                        }
                    }
                }  
            }
            // decorate           
            if (!hasHill) { 
                int xStart = xo;
                int xLength = xo + length;
                int s = random.nextInt(4);
                int e = random.nextInt(4);
                               
                if (hasBlocks && !hasStairs) {
                    if (floor - 4 > 0)
                    {
                        if ((xLength - 1 - e) - (xStart + 1 + s) > 2)
                        {
                            for (int x = xStart + 1 + s; x < xLength - 1 - e; x++)
                            {
                                if (x != xStart + 1 && x != xLength - 2 && ((PLAYER_PROFILE == KILLER && random.nextInt(2) == 0) || random.nextInt(3) == 0) && hasSpecialBlocks)
                                {
                                    if (random.nextInt(4) == 0 || (PLAYER_PROFILE == KILLER && random.nextInt(2) == 0))
                                    {
                                        setBlock(x, floor - 4, BLOCK_POWERUP);
                                        BLOCKS_POWER++;
                                    }
                                    else
                                    {	//the fills a block with a hidden coin
                                        setBlock(x, floor - 4, BLOCK_COIN);
                                        BLOCKS_COINS++;
                                    }
                                }
                                else if (random.nextInt(4) == 0)
                                {
                                    if (random.nextInt(4) == 0)
                                    {
                                        setBlock(x, floor - 4, (byte) (2 + 1 * 16));
                                    }
                                    else
                                    {
                                        setBlock(x, floor - 4, (byte) (1 + 1 * 16));
                                    }
                                }
                                else
                                {
                                    setBlock(x, floor - 4, BLOCK_EMPTY);
                                    BLOCKS_EMPTY++;
                                }
                            }
                        }
                    }
                }
                               
                if (hasEnemies && !hasPipes) {
                    int floorOffset = 1;
                    if (hasBlocks && random.nextInt(2) == 1) 
                        floorOffset = 4;
                    addEnemyLine(xStart + 1, xLength - 1, floor - floorOffset);
                }
                
                if (PLAYER_PROFILE == COIN_COLLECTOR) {
                    s = 0;
                    e = 0;
                } else {
                    s = random.nextInt(4);
                    e = random.nextInt(4);
                }
                if (hasCoins && !hasStairs) {
                    int floorOffset = 2;
                    if (hasBlocks && random.nextInt(2) == 1) 
                        floorOffset = 5;
                    if (floor - floorOffset > 0){
                        if ((xLength - 1 - e) - (xStart + 1 + s) > 1){
                            int coinxleft = (xStart + 1 + s);
                            int coinxright = (xLength - 1 - e);
                            int coinlen = coinxright - coinxleft - 1;
                            int coinarchleft = coinlen / 2;
                            int coinarchright = coinarchleft;
                            boolean even = (coinlen % 2 == 0);
                            int c = 0;
                            int coiny = floor - floorOffset;
                            for(int x = coinxleft; x < coinxright; x++){
                                int tempcoiny = coiny;
                                if (floor - floorOffset - coiny > 3)
                                    tempcoiny = floor - floorOffset - 3;
                                setBlock(x, tempcoiny, COIN);
                                COINS++;
                                if (hasCoinArch) {
                                    if (c < coinarchleft)
                                        coiny -= 1;
                                    else if ((!even && c > coinarchright) || (even && c >= coinarchright))
                                        coiny += 1;
                                    c++;
                                }
                            }
                        }
                    }
                }
            }
            
	        return length;
	    }

	    private void decorate(int xStart, int xLength, int floor)
	    {
	    	//if its at the very top, just return
	        if (floor < 1)
	        	return;

	        //boolean coins = random.nextInt(3) == 0;
	        boolean rocks = true;

	        //add an enemy line above the box
	        addEnemyLine(xStart + 1, xLength - 1, floor - 1);

	        int s = random.nextInt(4);
	        int e = random.nextInt(4);

	        if (floor - 2 > 0){
	            if ((xLength - 1 - e) - (xStart + 1 + s) > 1){
	                for(int x = xStart + 1 + s; x < xLength - 1 - e; x++){
	                    setBlock(x, floor - 2, COIN);
	                    COINS++;
	                }
	            }
	        }

	        s = random.nextInt(4);
	        e = random.nextInt(4);
	        
	        //this fills the set of blocks and the hidden objects inside them
	        if (floor - 4 > 0)
	        {
	            if ((xLength - 1 - e) - (xStart + 1 + s) > 2)
	            {
	                for (int x = xStart + 1 + s; x < xLength - 1 - e; x++)
	                {
	                    if (rocks)
	                    {
	                        if (x != xStart + 1 && x != xLength - 2 && random.nextInt(3) == 0)
	                        {
	                            if (random.nextInt(4) == 0)
	                            {
	                                setBlock(x, floor - 4, BLOCK_POWERUP);
	                                BLOCKS_POWER++;
	                            }
	                            else
	                            {	//the fills a block with a hidden coin
	                                setBlock(x, floor - 4, BLOCK_COIN);
	                                BLOCKS_COINS++;
	                            }
	                        }
	                        else if (random.nextInt(4) == 0)
	                        {
	                            if (random.nextInt(4) == 0)
	                            {
	                                setBlock(x, floor - 4, (byte) (2 + 1 * 16));
	                            }
	                            else
	                            {
	                                setBlock(x, floor - 4, (byte) (1 + 1 * 16));
	                            }
	                        }
	                        else
	                        {
	                            setBlock(x, floor - 4, BLOCK_EMPTY);
	                            BLOCKS_EMPTY++;
	                        }
	                    }
	                }
	            }
	        }
	    }

	    private void fixWalls()
	    {
	        boolean[][] blockMap = new boolean[width + 1][height + 1];

	        for (int x = 0; x < width + 1; x++)
	        {
	            for (int y = 0; y < height + 1; y++)
	            {
	                int blocks = 0;
	                for (int xx = x - 1; xx < x + 1; xx++)
	                {
	                    for (int yy = y - 1; yy < y + 1; yy++)
	                    {
	                        if (getBlockCapped(xx, yy) == GROUND){
	                        	blocks++;
	                        }
	                    }
	                }
	                blockMap[x][y] = blocks == 4;
	            }
	        }
	        blockify(this, blockMap, width + 1, height + 1);
	    }

	    private void blockify(Level level, boolean[][] blocks, int width, int height){
	        int to = 0;
	        if (type == LevelInterface.TYPE_CASTLE)
	        {
	            to = 4 * 2;
	        }
	        else if (type == LevelInterface.TYPE_UNDERGROUND)
	        {
	            to = 4 * 3;
	        }

	        boolean[][] b = new boolean[2][2];

	        for (int x = 0; x < width; x++)
	        {
	            for (int y = 0; y < height; y++)
	            {
	                for (int xx = x; xx <= x + 1; xx++)
	                {
	                    for (int yy = y; yy <= y + 1; yy++)
	                    {
	                        int _xx = xx;
	                        int _yy = yy;
	                        if (_xx < 0) _xx = 0;
	                        if (_yy < 0) _yy = 0;
	                        if (_xx > width - 1) _xx = width - 1;
	                        if (_yy > height - 1) _yy = height - 1;
	                        b[xx - x][yy - y] = blocks[_xx][_yy];
	                    }
	                }

	                if (b[0][0] == b[1][0] && b[0][1] == b[1][1])
	                {
	                    if (b[0][0] == b[0][1])
	                    {
	                        if (b[0][0])
	                        {
	                            level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
	                        }
	                        else
	                        {
	                            // KEEP OLD BLOCK!
	                        }
	                    }
	                    else
	                    {
	                        if (b[0][0])
	                        {
	                        	//down grass top?
	                            level.setBlock(x, y, (byte) (1 + 10 * 16 + to));
	                        }
	                        else
	                        {
	                        	//up grass top
	                            level.setBlock(x, y, (byte) (1 + 8 * 16 + to));
	                        }
	                    }
	                }
	                else if (b[0][0] == b[0][1] && b[1][0] == b[1][1])
	                {
	                    if (b[0][0])
	                    {
	                    	//right grass top
	                        level.setBlock(x, y, (byte) (2 + 9 * 16 + to));
	                    }
	                    else
	                    {
	                    	//left grass top
	                        level.setBlock(x, y, (byte) (0 + 9 * 16 + to));
	                    }
	                }
	                else if (b[0][0] == b[1][1] && b[0][1] == b[1][0])
	                {
	                    level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
	                }
	                else if (b[0][0] == b[1][0])
	                {
	                    if (b[0][0])
	                    {
	                        if (b[0][1])
	                        {
	                            level.setBlock(x, y, (byte) (3 + 10 * 16 + to));
	                        }
	                        else
	                        {
	                            level.setBlock(x, y, (byte) (3 + 11 * 16 + to));
	                        }
	                    }
	                    else
	                    {
	                        if (b[0][1])
	                        {
	                        	//right up grass top
	                            level.setBlock(x, y, (byte) (2 + 8 * 16 + to));
	                        }
	                        else
	                        {
	                        	//left up grass top
	                            level.setBlock(x, y, (byte) (0 + 8 * 16 + to));
	                        }
	                    }
	                }
	                else if (b[0][1] == b[1][1])
	                {
	                    if (b[0][1])
	                    {
	                        if (b[0][0])
	                        {
	                        	//left pocket grass
	                            level.setBlock(x, y, (byte) (3 + 9 * 16 + to));
	                        }
	                        else
	                        {
	                        	//right pocket grass
	                            level.setBlock(x, y, (byte) (3 + 8 * 16 + to));
	                        }
	                    }
	                    else
	                    {
	                        if (b[0][0])
	                        {
	                            level.setBlock(x, y, (byte) (2 + 10 * 16 + to));
	                        }
	                        else
	                        {
	                            level.setBlock(x, y, (byte) (0 + 10 * 16 + to));
	                        }
	                    }
	                }
	                else
	                {
	                    level.setBlock(x, y, (byte) (0 + 1 * 16 + to));
	                }
	            }
	        }
	    }
	    
	    public RandomLevel clone() throws CloneNotSupportedException {

	    	RandomLevel clone=new RandomLevel(width, height);

	    	clone.xExit = xExit;
	    	clone.yExit = yExit;
	    	byte[][] map = getMap();
	    	SpriteTemplate[][] st = getSpriteTemplate();
	    	
	    	for (int i = 0; i < map.length; i++)
	    		for (int j = 0; j < map[i].length; j++) {
	    			clone.setBlock(i, j, map[i][j]);
	    			clone.setSpriteTemplate(i, j, st[i][j]);
	    	}
	    	clone.BLOCKS_COINS = BLOCKS_COINS;
	    	clone.BLOCKS_EMPTY = BLOCKS_EMPTY;
	    	clone.BLOCKS_POWER = BLOCKS_POWER;
	    	clone.ENEMIES = ENEMIES;
	    	clone.COINS = COINS;
	    	
	        return clone;

	      }


}
